package de.XPortLib;

import java.util.AbstractQueue;
import java.util.Calendar;
import java.util.concurrent.ConcurrentLinkedQueue;

import de.XPortLib.Exceptions.DataTransmissionException;
import de.XPortLib.Exceptions.TCPCommunicationException;
import de.XPortLib.Interfaces.XPortCallbackIF;
import de.XPortLib.Interfaces.XPortControllerAsyncIF;

public class XPortControllerAsync extends XPortController implements XPortControllerAsyncIF {
	/** used for normal commands */
	protected final AbstractQueue<Runnable> commandQueue;
	/** used for changing port and ip which should have priority */
	protected final AbstractQueue<Runnable> priorityQueue;
	private Thread executionThread;
	
	/** saves the instance for this singleton */
	private volatile static XPortControllerAsync instance;
	
	private XPortControllerAsync() {
		super();
		commandQueue = new ConcurrentLinkedQueue<Runnable>();
		priorityQueue = new ConcurrentLinkedQueue<Runnable>();
		executionThread = new Thread("XPortAsyncExecutor") {
			@Override
			public void run() {
				while(!interrupted()) {
					//execute priority commands first
					while(!priorityQueue.isEmpty()) {
						priorityQueue.poll().run();
					}
					if (commandQueue.isEmpty()) {
						try {
							//wait if nothing to execute
							sleep(50);
						}
						catch (InterruptedException e) { }
					}
					else {
						//execute next task
						commandQueue.poll().run();
					}
				}
			}
		};
		executionThread.start();
	}
	
	public static XPortControllerAsync getInstance() {
		if (instance == null) {
			synchronized (XPortControllerAsync.class) {
				if (instance == null) {
					instance = new XPortControllerAsync();
				}		
			}
		}
		return instance;
	}
	
	/* (non-Javadoc)
	 * @see de.XPortLib.XportControllerAsyncIF#getIpAddress()
	 */
	@Override
	public String getIpAddress() {
		return super.getIpAddress();
	}

	/* (non-Javadoc)
	 * @see de.XPortLib.XportControllerAsyncIF#setIpAddress(java.lang.String)
	 */
	@Override
	public void setIpAddress(final String ipAddress) {
		priorityQueue.add(new Runnable() {
			public void run() {
				executor.setIpAddress(ipAddress);
			}
		});
	}

	/* (non-Javadoc)
	 * @see de.XPortLib.XportControllerAsyncIF#getPort()
	 */
	@Override
	public int getPort() {
		return super.getPort();
	}

	/* (non-Javadoc)
	 * @see de.XPortLib.XportControllerAsyncIF#setPort(int)
	 */
	@Override
	public void setPort(final int port) {
		priorityQueue.add(new Runnable() {
			public void run() {
				executor.setPort(port);
			}
		});
	}

	
	/* (non-Javadoc)
	 * @see de.XPortLib.XportControllerAsyncIF#readRelaisAsync(de.XPortLib.Interfaces.XPortCallbackIF)
	 */
	public void readRelaisAsync(final XPortCallbackIF callback) {
		commandQueue.add(new Runnable() {
			public void run() {
				try {
					boolean[] status;
					status = readRelais();
					callback.updateRelaisStatus(status);
				} catch (DataTransmissionException e) {
					callback.exceptionOccoured(e);
				} catch (TCPCommunicationException e) {
					callback.exceptionOccoured(e);
				}

			}
		});
	}

	
	/* (non-Javadoc)
	 * @see de.XPortLib.XportControllerAsyncIF#toggleRelaisAsync(int, de.XPortLib.Interfaces.XPortCallbackIF)
	 */
	public void toggleRelaisAsync(final int channel, final XPortCallbackIF callback) {
		commandQueue.add(new Runnable() {
			public void run() {
				try {
					boolean[] status;
					status = toggleRelais(channel);
					callback.updateRelaisStatus(status);
				} catch (DataTransmissionException e) {
					callback.exceptionOccoured(e);
				} catch (TCPCommunicationException e) {
					callback.exceptionOccoured(e);
				}

			}
		});
	}
	
	
	/* (non-Javadoc)
	 * @see de.XPortLib.XportControllerAsyncIF#readTimerAsync(int, de.XPortLib.Interfaces.XPortCallbackIF)
	 */
	public void readTimerAsync(final int nr, final XPortCallbackIF callback) {
		commandQueue.add(new Runnable() {
			public void run() {
				try {
					TimerEvent timer;
					timer = readTimer(nr);
					callback.updateTimer(timer);
				} catch (DataTransmissionException e) {
					callback.exceptionOccoured(e);
				} catch (TCPCommunicationException e) {
					callback.exceptionOccoured(e);
				}

			}
		});		
	}
	
	
	/* (non-Javadoc)
	 * @see de.XPortLib.XportControllerAsyncIF#readTimers(de.XPortLib.Interfaces.XPortCallbackIF)
	 */
	public void readTimersAsync(final XPortCallbackIF callback) {
		commandQueue.add(new Runnable() {
			public void run() {
				try {
					TimerEvent[] timers;
					timers = readTimers();
					callback.updateTimers(timers);
				} catch (DataTransmissionException e) {
					callback.exceptionOccoured(e);
				} catch (TCPCommunicationException e) {
					callback.exceptionOccoured(e);
				}

			}
		});	
	}
	

	/* (non-Javadoc)
	 * @see de.XPortLib.XportControllerAsyncIF#writeTimerAsync(int, de.XPortLib.TimerEvent, de.XPortLib.Interfaces.XPortCallbackIF)
	 */
	public void writeTimerAsync(final int nr, final TimerEvent timer, final XPortCallbackIF callback) {
		commandQueue.add(new Runnable() {
			public void run() {
				try {
					int totalTimers;
					totalTimers = writeTimer(nr, timer);
					callback.updateTimerCount(totalTimers);
				} catch (DataTransmissionException e) {
					callback.exceptionOccoured(e);
				} catch (TCPCommunicationException e) {
					callback.exceptionOccoured(e);
				}

			}
		});			
	}
	
	/* (non-Javadoc)
	 * @see de.XPortLib.XportControllerAsyncIF#deleteTimerAsync(int, de.XPortLib.Interfaces.XPortCallbackIF)
	 */
	public void deleteTimerAsync(final int nr, final XPortCallbackIF callback) {
		commandQueue.add(new Runnable() {
			public void run() {
				try {
					int totalTimers;
					totalTimers = deleteTimer(nr);
					callback.updateTimerCount(totalTimers);
				} catch (DataTransmissionException e) {
					callback.exceptionOccoured(e);
				} catch (TCPCommunicationException e) {
					callback.exceptionOccoured(e);
				}

			}
		});			
		
	}

	
	/* (non-Javadoc)
	 * @see de.XPortLib.XportControllerAsyncIF#setDateTimeAsync(java.util.Calendar, de.XPortLib.Interfaces.XPortCallbackIF)
	 */
	public void setDateTimeAsync(final Calendar cal, final XPortCallbackIF callback) {
		commandQueue.add(new Runnable() {
			public void run() {
				try {
					setDateTime(cal);
				} catch (DataTransmissionException e) {
					callback.exceptionOccoured(e);
				} catch (TCPCommunicationException e) {
					callback.exceptionOccoured(e);
				}

			}
		});		
	}

}
